/*
 * TouchControl.java
 *
 * Created on 18 Январь 2010 г., 18:42
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package jimm.ui.base;

import java.util.*;

/**
 *
 * @author Vladimir Kryukov
 */
// #sijapp cond.if modules_TOUCH is "true"#
public class TouchControl {
    private static final int discreteness = 50;

    private boolean isDraged;
    private int startY;
    private int startX;
    private int curY;
    private int curX;
    private long pressTime;

    
    private boolean scrolling;
    private int scrollPrevTop;
    
    public boolean touchUsed;
    
    private boolean horizontalDirection;
    private boolean directionUnlock;

    public int prevTopY;
    public boolean isSecondTap;

    private boolean kineticOn;
    private volatile KineticScrolling kinetic = null;
    private int kineticFromY;
    private long kineticStartTime;
    private long kineticLastTime;

    private int pressLimit;
    private int movingLimit;
        
    private CanvasEx canvas = null;
    private int viewHeight = 0;
    private int viewWidth = 0;
    
    public void setCanvas(CanvasEx c) {
        stopKinetic();
        pressLimit = CanvasEx.minItemHeight / 2;
        movingLimit = Math.max(2, pressLimit / 2);
        canvas = c;
    }
    public void setView(int width, int height) {
        viewWidth = width;
        viewHeight = height;
    }
    private boolean isDraged(int x, int y) {
        return (pressLimit < Math.abs(x - startX))
                || (pressLimit < Math.abs(y - startY));
    }
    private void updateDirection() {
        if (directionUnlock) {
            horizontalDirection = (Math.abs(startY - curY) * 2 < Math.abs(startX - curX));
            directionUnlock = false;
        }
    }
    
    private boolean between(int from, int to, int value) {
        return (from <= value) && (value <= to);
    }
    private boolean isScroll(int x, int y) {
        int[] scroll = canvas.getScroll();
        return (null != scroll)
                && between(scroll[GraphicsEx.SCROLL_TOP],
                scroll[GraphicsEx.SCROLL_TOP] + scroll[GraphicsEx.SCROLL_HEIGHT],
                y)
                && between(scroll[GraphicsEx.SCROLL_LEFT] - scroll[GraphicsEx.SCROLL_WIDTH],
                scroll[GraphicsEx.SCROLL_LEFT] + 2 * scroll[GraphicsEx.SCROLL_WIDTH],
                x);
    }
    private void doScrolling(int fromY, int toY) {
        CanvasEx c = canvas;
        int[] scroll = c.getScroll();
        int scrollable = scroll[GraphicsEx.SCROLL_TOTAL]
                - scroll[GraphicsEx.SCROLL_VISIBLE_ITEMS];
        if (0 == scrollable) {
            return;
        }

        int y = scroll[GraphicsEx.SCROLL_TOP];
        int height = scroll[GraphicsEx.SCROLL_HEIGHT];
        int pos = this.scrollPrevTop;
        int len = scroll[GraphicsEx.SCROLL_VISIBLE_ITEMS];
        int total = scroll[GraphicsEx.SCROLL_TOTAL];
        
        int scrollY1 = (pos * height) / total;
        int scrollY2 = ((pos + len) * height) / total;
        int sliderSize = scrollY2 - scrollY1;
        if (sliderSize < 7) {
            sliderSize = 7;
            scrollY2 = Math.min(height, scrollY1 + sliderSize);
            scrollY1 = Math.max(0, scrollY2 - sliderSize);
        }
        scrollY1 += y;
        scrollY2 += y;
        if ((fromY < scrollY1) || (scrollY2 < fromY)) {
           return;
        }
        
        int delta = (toY - fromY);
        int scrollHeight = scrollable * height / total;
        int offset = scrollHeight * scrollPrevTop / scrollable;
        int newTopScroll = scrollable * (offset + delta) / scrollHeight;
        scroll[GraphicsEx.SCROLL_TOP_VALUE] = newTopScroll;
        c.setScrollTop(newTopScroll);
    }

    public synchronized void pointerPressed(int x, int y) {
        stopKinetic();
        scrolling = false;

        kineticOn = true;

        horizontalDirection = false;
        directionUnlock = true;
        isDraged = false;
        startX = x;
        startY = y;
        curX = x;
        curY = y;
        long now = System.currentTimeMillis();
        pressTime = now;
        kineticStartTime = now;
        kineticFromY = y;

        if (viewHeight <= y) {
            kineticOn = false;
            return;
        }
        CanvasEx c = canvas;
        if (null != c) {
            if (isScroll(x, y)) {
                scrolling = true;
                scrollPrevTop = c.getScrollTop();
                return;
            }
            try {
                c.stylusPressed(x, y);
            } catch (Exception e) {
                kineticOn = false;
                // #sijapp cond.if modules_DEBUGLOG is "true" #
                jimm.modules.DebugLog.panic("stylusPressed");
                // #sijapp cond.end #
            }
        }
    }

    public synchronized void pointerReleased(int x, int y) {
        isDraged |= isDraged(x, y);
        
        curX = x;
        curY = y;
        if (viewHeight <= y) {
            if (isDraged) {
                return;
            }
            int w = viewWidth;
            int lsoftWidth = w / 2 - (w * 10 / 100);
            int rsoftWidth = w - lsoftWidth;
            NativeCanvas nat = NativeCanvas.getInstance();
            if (x < lsoftWidth) {
                nat.emulateKey(NativeCanvas.LEFT_SOFT);
                
            } else if (rsoftWidth < x) {
                nat.emulateKey(NativeCanvas.RIGHT_SOFT);
                
            } else {
                nat.emulateKey(NativeCanvas.NAVIKEY_FIRE);
            }
            pressTime = 0;
            return;
        }
        CanvasEx c = canvas;
        if (null != c) {
            if (isDraged) {
                if (scrolling) {
                    doScrolling(startY, y);
                    return;
                }
                updateDirection();
                try {
                    if (horizontalDirection) {
                        c.stylusXMoved(startX, startY, x, y);
                        kineticOn = false;
                    } else {
                        c.stylusYMoved(startX, startY, x, y);
                    }
                    if (kineticOn) {
                        long deltaT = System.currentTimeMillis() - kineticStartTime;
                        startKinetic(kineticFromY, y, (int)deltaT);
                    }
                } catch (Exception e) {
                    // #sijapp cond.if modules_DEBUGLOG is "true" #
                    jimm.modules.DebugLog.panic("stylusMoved");
                    // #sijapp cond.end #
                }
            } else {
                try {
                    if (scrolling) {
                        scrolling = false;
                        if (isScroll(x, y)) {
                            return;
                        }
                        c.stylusPressed(startX, startY);
                    }
                    c.stylusTap(x, y, (System.currentTimeMillis() - pressTime) > 700);
                } catch (Exception e) {
                    // #sijapp cond.if modules_DEBUGLOG is "true" #
                    jimm.modules.DebugLog.panic("stylusTap");
                    // #sijapp cond.end #
                }
            }
        }
        pressTime = 0;
    }

    public synchronized void pointerDragged(int x, int y) {
        boolean moved = (movingLimit < Math.abs(x - curX))
                || (movingLimit < Math.abs(y - curY));
        if (!moved) {
            return;
        }
        isDraged |= isDraged(x, y);

        CanvasEx c = canvas;
        if (isDraged && (0 < pressTime) && (null != c)) {
            if (kineticOn) {
                kineticStartTime = kineticLastTime;
                kineticFromY = curY;
                kineticLastTime = System.currentTimeMillis();
            }
            curX = x;
            curY = y;
            updateDirection();
            try {
                if (scrolling) {
                    doScrolling(startY, y);
                    return;
                }
                if (horizontalDirection) {
                    c.stylusXMoving(startX, startY, x, y);
                } else {
                    c.stylusYMoving(startX, startY, x, y);
                }
            } catch (Exception e) {
                // #sijapp cond.if modules_DEBUGLOG is "true" #
                jimm.modules.DebugLog.panic("stylusMoving");
                // #sijapp cond.end #
            }
        }
    }

    synchronized void kineticMoving(int y) {
        CanvasEx c = canvas;
        if (kineticOn && (null != c) && (null != kinetic)) {
            try {
                c.stylusKineticYMoving(startX, startY, startX, y);
            } catch (Exception e) {
                // #sijapp cond.if modules_DEBUGLOG is "true" #
                jimm.modules.DebugLog.panic("stylusKineticMoving");
                // #sijapp cond.end #
            }
        }
    }

    
    
    private int calcV(int way, int time) {
        // v = 2 * s / t - v0
        // v0 = 0
        return 2 * way * discreteness / time;
    }
    private int calcAbsA(int v, int time) {
        // a = (v1 - v0) / t
        // v0 = 0
        return Math.abs(v * discreteness / time);
    }
    private void startKinetic(int fromY, int toY, int time) {
        final int way = toY - fromY;
        if ((Math.abs(way) < 3) || (time < 2)) {
            return;
        }
        final int v = calcV(way, time) * 4 / 5;
        if (Math.abs(v) <= 2) {
            return;
        }
        int a = Math.max(1, Math.max(calcAbsA(v, time) / 3, calcAbsA(v, 3000)));
        KineticScrolling it = new KineticScrolling(this, toY, v, ((way < 0) ? +a : -a));
        it.start(discreteness);
        kinetic = it;
    }
    private void stopKinetic() {
        KineticScrolling k = kinetic;
        kinetic = null;
        if (null != k) {
            k.stop();
        }
    }
}

class KineticScrolling extends TimerTask {
    private int y;
    private int a;
    private int v;
    private Timer timer;
    private TouchControl touch;
    
    public KineticScrolling(TouchControl touch, int y, int velosity, int acceleration) {
        this.y = y;
        this.a = acceleration;
        this.v = velosity;
        this.touch = touch;
        timer = new Timer();
    }
    public void start(int interval) {
        timer.schedule(this, interval, interval);
    }

    public void run() {
        y += v;
        TouchControl t = touch;
        if (null != t) {
            t.kineticMoving(y);
        }
        int prevV = v;
        v += a;
        
        if (Math.abs(v) <= Math.min(5, CanvasEx.minItemHeight / 4)) {
            stop();
        
        } else if (v * prevV < 0) {
            stop();
        }
    }
    public void stop() {
        cancel();
        touch = null;
        Timer t = timer;
        timer = null;
        if (null != t) {
            t.cancel();
        }
    }
}
// #sijapp cond.end#